"""
插件管理器
负责发现、加载、启停、卸载、依赖注入
"""

import os
import sys
import importlib.util
import logging
from typing import Any, Dict, List, Optional
from .plugin_base import PluginBase
from .config_loader import ConfigLoader, PluginConfig, PluginConfigError
from .api_context import APIContext
from .hook_hub import HookHub


class PluginLoadError(Exception):
    """插件加载错误"""
    def __init__(self, message: str, plugin_name: str) -> None:
        super().__init__(message)
        self.plugin_name = plugin_name


class PluginDependencyMissing(Exception):
    """插件依赖缺失"""
    def __init__(self, plugin_name: str, missing_deps: List[str]) -> None:
        super().__init__(f"Plugin {plugin_name} missing dependencies: {missing_deps}")
        self.plugin_name = plugin_name
        self.missing_deps = missing_deps


class PluginManager:
    """
    插件管理器
    
    核心功能：
    - scan(): 扫描插件目录
    - load(name): 加载指定插件
    - unload(name): 卸载指定插件
    - get(name): 获取插件实例
    - reload(name): 重新加载插件
    - enable/disable: 启用/禁用插件
    """
    
    def __init__(
        self,
        plugin_dir: str,
        api_context: Optional[APIContext] = None,
        hook_hub: Optional[HookHub] = None
    ) -> None:
        """
        初始化插件管理器
        
        Args:
            plugin_dir: 插件根目录
            api_context: API上下文（如不提供则自动创建）
            hook_hub: 钩子中心（如不提供则自动创建）
        """
        self.plugin_dir = plugin_dir
        self.api_context = api_context or APIContext("PluginSystem")
        self.hook_hub = hook_hub or HookHub()
        
        self._plugins: Dict[str, PluginBase] = {}
        self._configs: Dict[str, PluginConfig] = {}
        self._modules: Dict[str, Any] = {}
        self._logger = logging.getLogger(__name__)
        
        # 确保插件目录存在
        os.makedirs(plugin_dir, exist_ok=True)
    
    # ========== 扫描和发现 ==========
    
    def scan(self, auto_load: bool = False) -> List[str]:
        """
        扫描插件目录
        
        Args:
            auto_load: 是否自动加载所有插件
            
        Returns:
            发现的插件名称列表
        """
        plugin_dirs = ConfigLoader.discover_plugins(self.plugin_dir)
        discovered_plugins = []
        
        for plugin_path in plugin_dirs:
            try:
                config_path = os.path.join(plugin_path, 'plugin.yml')
                config = ConfigLoader.load_from_file(config_path)
                
                self._configs[config.name] = config
                discovered_plugins.append(config.name)
                
                self._logger.info(
                    f"Discovered plugin: {config.name} v{config.version}"
                )
                
                # 自动加载
                if auto_load and config.enabled:
                    try:
                        self.load(config.name)
                    except Exception as e:
                        self._logger.error(
                            f"Failed to auto-load {config.name}: {e}"
                        )
            except Exception as e:
                self._logger.error(
                    f"Failed to load config from {plugin_path}: {e}"
                )
        
        # 按优先级排序
        discovered_plugins.sort(
            key=lambda name: self._configs[name].priority,
            reverse=True
        )
        
        return discovered_plugins
    
    # ========== 加载和卸载 ==========
    
    def load(self, name: str) -> PluginBase:
        """
        加载插件
        
        Args:
            name: 插件名称
            
        Returns:
            插件实例
            
        Raises:
            PluginLoadError: 加载失败
            PluginDependencyMissing: 依赖缺失
        """
        # 检查是否已加载
        if name in self._plugins:
            self._logger.warning(f"Plugin {name} already loaded")
            return self._plugins[name]
        
        # 获取配置
        if name not in self._configs:
            raise PluginLoadError(f"Plugin config not found: {name}", name)
        
        config = self._configs[name]
        
        # 检查依赖
        missing_deps = self._check_dependencies(config.dependencies)
        if missing_deps:
            raise PluginDependencyMissing(name, missing_deps)
        
        try:
            # 动态导入模块
            plugin_module = self._import_plugin(name, config)
            
            # 获取入口类
            entry_class = getattr(plugin_module, config.entry_class, None)
            if entry_class is None:
                raise PluginLoadError(
                    f"Entry class not found: {config.entry_class}",
                    name
                )
            
            # 实例化插件
            plugin_instance = entry_class()
            
            # 设置基本属性
            plugin_instance.name = config.name
            plugin_instance.version = config.version
            plugin_instance.author = config.author
            plugin_instance.description = config.description
            
            # 创建插件专用上下文
            plugin_context = self.api_context.create_child_context(name)
            
            # 注入配置
            for key, value in config.config.items():
                plugin_context.set_config(key, value)
            
            # 调用on_load
            plugin_instance.on_load(plugin_context)
            plugin_instance.enabled = True
            
            # 保存
            self._plugins[name] = plugin_instance
            self._modules[name] = plugin_module
            
            # 触发钩子
            self.hook_hub.emit('plugin_loaded', plugin_name=name)
            
            self._logger.info(f"Plugin loaded: {name}")
            
            return plugin_instance
            
        except Exception as e:
            raise PluginLoadError(f"Failed to load plugin: {e}", name)
    
    def unload(self, name: str) -> bool:
        """
        卸载插件
        
        Args:
            name: 插件名称
            
        Returns:
            是否卸载成功
        """
        if name not in self._plugins:
            self._logger.warning(f"Plugin {name} not loaded")
            return False
        
        try:
            plugin = self._plugins[name]
            
            # 调用on_unload
            plugin.on_unload()
            plugin.enabled = False
            
            # 移除
            del self._plugins[name]
            
            # 移除模块
            if name in self._modules:
                module_name = self._modules[name].__name__
                if module_name in sys.modules:
                    del sys.modules[module_name]
                del self._modules[name]
            
            # 触发钩子
            self.hook_hub.emit('plugin_unloaded', plugin_name=name)
            
            self._logger.info(f"Plugin unloaded: {name}")
            
            return True
            
        except Exception as e:
            self._logger.error(f"Failed to unload plugin {name}: {e}")
            return False
    
    def reload(self, name: str) -> PluginBase:
        """
        重新加载插件
        
        Args:
            name: 插件名称
            
        Returns:
            新的插件实例
        """
        self.unload(name)
        return self.load(name)
    
    # ========== 获取和查询 ==========
    
    def get(self, name: str) -> Optional[PluginBase]:
        """
        获取插件实例
        
        Args:
            name: 插件名称
            
        Returns:
            插件实例，不存在返回None
        """
        return self._plugins.get(name)
    
    def get_all(self) -> Dict[str, PluginBase]:
        """获取所有已加载的插件"""
        return self._plugins.copy()
    
    def get_config(self, name: str) -> Optional[PluginConfig]:
        """获取插件配置"""
        return self._configs.get(name)
    
    def is_loaded(self, name: str) -> bool:
        """检查插件是否已加载"""
        return name in self._plugins
    
    def get_loaded_names(self) -> List[str]:
        """获取所有已加载插件的名称"""
        return list(self._plugins.keys())
    
    # ========== 启用和禁用 ==========
    
    def enable(self, name: str) -> bool:
        """
        启用插件
        
        Args:
            name: 插件名称
            
        Returns:
            是否成功
        """
        if name not in self._configs:
            return False
        
        self._configs[name].enabled = True
        
        # 如果已加载，直接启用
        if name in self._plugins:
            self._plugins[name].enabled = True
        else:
            # 否则尝试加载
            try:
                self.load(name)
            except Exception as e:
                self._logger.error(f"Failed to enable plugin {name}: {e}")
                return False
        
        return True
    
    def disable(self, name: str) -> bool:
        """
        禁用插件
        
        Args:
            name: 插件名称
            
        Returns:
            是否成功
        """
        if name not in self._configs:
            return False
        
        self._configs[name].enabled = False
        
        if name in self._plugins:
            self._plugins[name].enabled = False
        
        return True
    
    # ========== 依赖管理 ==========
    
    def _check_dependencies(self, dependencies: List[str]) -> List[str]:
        """
        检查依赖是否满足
        
        Args:
            dependencies: 依赖列表
            
        Returns:
            缺失的依赖列表
        """
        missing = []
        for dep in dependencies:
            if dep not in self._plugins:
                missing.append(dep)
        return missing
    
    def _import_plugin(self, name: str, config: PluginConfig) -> Any:
        """
        动态导入插件模块
        
        Args:
            name: 插件名称
            config: 插件配置
            
        Returns:
            导入的模块对象
        """
        # 构建模块路径
        plugin_path = None
        for item in os.listdir(self.plugin_dir):
            item_path = os.path.join(self.plugin_dir, item)
            if os.path.isdir(item_path):
                config_path = os.path.join(item_path, 'plugin.yml')
                if os.path.exists(config_path):
                    test_config = ConfigLoader.load_from_file(config_path)
                    if test_config.name == name:
                        plugin_path = item_path
                        break
        
        if not plugin_path:
            raise PluginLoadError(f"Plugin directory not found: {name}", name)
        
        # 查找主模块文件
        main_file = os.path.join(plugin_path, '__init__.py')
        if not os.path.exists(main_file):
            main_file = os.path.join(plugin_path, 'main.py')
        
        if not os.path.exists(main_file):
            raise PluginLoadError(
                f"Plugin main file not found (need __init__.py or main.py)",
                name
            )
        
        # 动态导入
        spec = importlib.util.spec_from_file_location(
            f"plugins.{name}",
            main_file
        )
        if spec is None or spec.loader is None:
            raise PluginLoadError(f"Failed to create module spec", name)
        
        module = importlib.util.module_from_spec(spec)
        sys.modules[spec.name] = module
        spec.loader.exec_module(module)
        
        return module